package gfun

import (
	"bufio"
	"fmt"
	"github.com/cavaliergopher/grab/v3"
	"github.com/grafov/m3u8"
	"github.com/schollz/progressbar/v3"
	"io"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
)

// Download 普通下载，断点续传
func Download(URL string, fileName, outPath string, proxyURL string) error {
	// 创建HTTP客户端并配置代理
	transport := &http.Transport{}
	if proxyURL != "" {
		proxy, err := url.Parse(proxyURL)
		if err != nil {
			return fmt.Errorf("代理地址解析失败: %v", err)
		}
		transport.Proxy = http.ProxyURL(proxy)
	}

	// 创建grab客户端
	client := grab.NewClient()
	client.HTTPClient = &http.Client{Transport: transport}

	// 创建请求
	req, err := grab.NewRequest(path.Join(outPath, fileName), URL)
	if err != nil {
		return fmt.Errorf("创建下载请求失败: %v", err)
	}

	// 设置请求头
	req.HTTPRequest.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
	req.HTTPRequest.Header.Set("Accept", "*/*")
	req.HTTPRequest.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
	req.HTTPRequest.Header.Set("Referer", "https://cmaker.sookmyung.ac.kr/em/63e9b83b44d40")

	fmt.Printf("开始下载: %v\n", req.URL())
	resp := client.Do(req)

	// 创建进度条
	bar := progressbar.NewOptions64(
		resp.Size(),
		progressbar.OptionSetDescription("下载进度"),
		progressbar.OptionEnableColorCodes(true),
		progressbar.OptionShowBytes(true),
		progressbar.OptionSetWidth(30),
		progressbar.OptionShowCount(),
	)

	// 监控下载进度
	ticker := time.NewTicker(100 * time.Millisecond)
	defer ticker.Stop()

	for !resp.IsComplete() {
		select {
		case <-ticker.C:
			bar.Set64(resp.BytesComplete())
		case <-resp.Done:
			break
		}
	}

	// 检查下载是否成功
	if err = resp.Err(); err != nil {
		return fmt.Errorf("下载失败: %v", err)
	}

	// 完成进度条
	bar.Finish()
	fmt.Printf("\n下载完成，保存到: %v\n", resp.Filename)
	return nil
}

// DownloadM3u8 下载m3u8
func DownloadM3u8(URL, fileName, outPath, proxyURL string) error {
	// 创建HTTP客户端
	transport := &http.Transport{}
	if proxyURL != "" {
		proxy, err := url.Parse(proxyURL)
		if err != nil {
			return fmt.Errorf("代理地址解析失败: %v", err)
		}
		transport.Proxy = http.ProxyURL(proxy)
	}
	client := &http.Client{Transport: transport}

	// 获取m3u8文件内容
	req, err := http.NewRequest("GET", URL, nil)
	if err != nil {
		return fmt.Errorf("创建请求失败: %v", err)
	}

	// 设置请求头
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
	req.Header.Set("Accept", "*/*")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")

	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("获取m3u8文件失败: %v", err)
	}
	defer resp.Body.Close()

	// 解析m3u8文件
	playlist, listType, err := m3u8.DecodeFrom(bufio.NewReader(resp.Body), false)
	if err != nil {
		return fmt.Errorf("解析m3u8文件失败: %v", err)
	}

	// 获取m3u8的基础URL
	baseURL := URL[:strings.LastIndex(URL, "/")]

	// 处理不同类型的播放列表
	switch listType {
	case m3u8.MASTER:
		masterpl := playlist.(*m3u8.MasterPlaylist)
		if len(masterpl.Variants) == 0 {
			return fmt.Errorf("master playlist 中没有可用的视频流")
		}
		// 选择第一个可用的视频流
		variant := masterpl.Variants[0]
		variantURL := variant.URI
		if !strings.HasPrefix(variantURL, "http") {
			variantURL = baseURL + "/" + variantURL
		}
		// 递归下载选中的视频流
		return DownloadM3u8(variantURL, fileName, outPath, proxyURL)

	case m3u8.MEDIA:
		mediapl := playlist.(*m3u8.MediaPlaylist)
		return downloadMediaPlaylist(client, mediapl, baseURL, fileName, outPath)

	default:
		return fmt.Errorf("未知的播放列表类型")
	}
}
func downloadMediaPlaylist(client *http.Client, mediapl *m3u8.MediaPlaylist, baseURL, fileName, outputPath string) error {
	// 创建临时目录存储分片
	tempDir := filepath.Join(filepath.Dir(outputPath), "temp_"+fileName)
	os.MkdirAll(tempDir, 0755)
	defer os.RemoveAll(tempDir)

	// 下载所有分片
	var segments []string
	totalSegments := 0
	for _, segment := range mediapl.Segments {
		if segment != nil {
			totalSegments++
		}
	}

	fmt.Printf("共发现 %d 个视频分片\n", totalSegments)
	bar := progressbar.NewOptions(totalSegments,
		progressbar.OptionSetDescription("下载分片进度"),
		progressbar.OptionEnableColorCodes(true),
		progressbar.OptionShowBytes(true),
		progressbar.OptionSetWidth(30),
		progressbar.OptionShowCount(),
	)

	for i, segment := range mediapl.Segments {
		if segment == nil {
			continue
		}

		// 构建分片URL
		segmentURL := segment.URI
		if !strings.HasPrefix(segmentURL, "http") {
			segmentURL = baseURL + "/" + segmentURL
		}

		// 下载分片
		segmentPath := filepath.Join(tempDir, fmt.Sprintf("segment_%d.ts", i))
		if err := downloadSegment(client, segmentURL, segmentPath); err != nil {
			return fmt.Errorf("下载分片失败 %d: %v", i, err)
		}
		segments = append(segments, segmentPath)
		bar.Add(1)
	}

	fmt.Println("\n开始合并视频分片...")
	return mergeSegments(segments, path.Join(outputPath, fileName))
}
func downloadSegment(client *http.Client, url, outputPath string) error {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")

	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	out, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, resp.Body)
	return err
}
func mergeSegments(segments []string, outputPath string) error {
	out, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	defer out.Close()

	for _, segment := range segments {
		data, err := os.ReadFile(segment)
		if err != nil {
			return err
		}
		if _, err := out.Write(data); err != nil {
			return err
		}
	}

	return nil
}
